module SolverElementList

    integer, parameter :: FirstSolutionRow = 1
    integer, parameter :: SecondSolutionRow = 2
    integer, parameter :: BulkSolutionRow = 3


    type SolverList
        integer, dimension(:), allocatable :: first
        integer, dimension(:), allocatable :: second
        integer, dimension(:), allocatable :: bulk

        integer :: biggestIndex
        integer :: smallestIndex

        integer :: nFirst
        integer :: nSecond
        integer :: nBulk

        integer :: actualFirst !< Controls the index of the lattest solved item on the first group of itens to solve
        integer :: actualSecond !< Controls the index of the lattest solved item on the second group of itens to solve
        integer :: actualBulk
    end type

contains


    subroutine StartSolverList(List, maxFirst, maxSecond, maxBulk, smallest, biggest)
        implicit none
        type(SolverList), intent(out) :: List
        integer, intent(in) :: maxFirst
        integer, intent(in) :: maxSecond
        integer, intent(in) :: maxBulk
        integer, intent(in) :: smallest
        integer, intent(in) :: biggest

        integer :: rStatus

        allocate(List%first(maxFirst), stat = rStatus)

        if(rStatus /= 0) then
            print *, 'Error while trying to allocate memory for first list of solver.'
            return
        end if

        allocate(List%second(maxSecond), stat = rStatus)

        if(rStatus /= 0) then
            print *, 'Error while trying to allocate memory for second list of solver.'
            return
        end if

        allocate(List%bulk(maxBulk), stat = rStatus)

        if(rStatus /= 0) then
            print *, 'Error while trying to allocate memory for bulk list of solver.'
            return
        end if

        if(smallest <= biggest ) then
            List % smallestIndex = smallest
            List % biggestIndex = biggest
        else
            print *, 'Error while trying to apply minimal and maximal index value. Smallest index is bigger than the biggest.'
            return
        end if

        List % actualFirst = 0
        List % actualSecond = 0
        List % actualBulk = 0
    end subroutine

    subroutine EraseSolverList(List)
        implicit none
        type(SolverList), intent(out) :: List

        deallocate(List % first)
        deallocate(List % second)
        deallocate(List % bulk)

        List % nFirst = 0
        List % nSecond = 0
        List % nBulk = 0

        List % smallestIndex = 0
        List % biggestIndex = 0
    end subroutine



    pure function IndexIsValid(List, ItemIndex) result (lvalue)
        implicit none
        type(SolverList), intent(in) :: List
        integer, intent(in) :: ItemIndex

        logical :: lvalue

        lvalue = (List % smallestIndex <= ItemIndex .and. ItemIndex <= List % biggestIndex)
    end function



    subroutine AddToSolverList(List, Destination, ItemIndex)
        implicit none
        type(SolverList), intent(out) :: List
        integer, intent(in) :: Destination
        integer, intent(in) :: ItemIndex

        select case(Destination)
            case(FirstSolutionRow)
                if(IndexIsValid(List, ItemIndex) .and. size(List%first) <= List%nFirst+1) then
                    List % first(List%nFirst+1) = ItemIndex
                    List%nFirst = List%nFirst+1
                end if
            case(SecondSolutionRow)
                if(IndexIsValid(List, ItemIndex) .and. size(List%second) <= List%nSecond+1) then
                    List % second(List%nSecond+1) = ItemIndex
                    List%nSecond = List%nSecond+1
                end if
            case(BulkSolutionRow)
                if(IndexIsValid(List, ItemIndex) .and. size(List%bulk) <= List%nBulk+1) then
                    List % bulk(List%nBulk+1) = ItemIndex
                    List%nBulk = List%nBulk+1
                end if
            case default
                print *, 'Invalid Destination for sublist of solver.'
        end select
    end subroutine

    !> \brief Return the position of the index on the list. If it is not, then returns 0
    !!
    !! \param List
    !! \param
    !! \return
    !!
    !!
    pure function IsOnList(List, Destination, ItemIndex) result (lvalue)
        implicit none
        type(SolverList), intent(in) :: List
        integer, intent(in) :: Destination
        integer, intent(in) :: ItemIndex

        integer :: lvalue
        integer :: n
        select case(Destination)
            case(FirstSolutionRow)
                do lvalue = 1, List % nFirst
                    if(List%first(lvalue) == ItemIndex) then
                        return
                    end if
                end do
                lvalue = 0
            case(SecondSolutionRow)
                do lvalue = 1, List % nSecond
                    if(List%second(lvalue) == ItemIndex) then
                        return
                    end if
                end do
            case(BulkSolutionRow)
                do lvalue = 1, List % nBulk
                    if(List%bulk(lvalue) == ItemIndex) then
                        return
                    end if
                end do
        end select
    end function

    !> \brief Function that returns the next Item Index to Solv
    !!
    !! \param List - The List of Itens to solve
    !! \param Destination - The List where the next element should be found
    !! \return lvalue - The index of the element
    function NextIndex(List, Destination) result (lvalue)
        implicit none
        type(SolverList), intent(inout) :: List
        integer, intent(in) :: Destination

		integer :: lvalue

        select case(Destination)
            case(FirtSolutionRow)
				if (List % actualFirst == List % nFirst) then
					List % actualFirst = 1
				else
					List % actualFirst = List % actualFirst + 1
				end if
				lvalue = List % first(List % actualFirst)
            case(SecondSolutionRow)
				if (List % actualSecond == List % nSecond) then
					List % actualSecond = 1
				else
					List % actualSecond = List % actualSecond + 1
				end if
				List % actualSecond = List % actualSecond + 1
				lvalue = List % second(List % actualSecond)
            case(BulkSolutionRow)
				if (List % actualBulk == List % nBulk) then
					List % actualBulk = 1
				else
					List % actualBulk = List % actualBulk + 1
				end if
				List % actualBulk = List % actualBulk + 1
				lvalue = List % bulk(List % actualBulk)
        end select
    end function


	pure function IsFinished(List, Destination) result (lvalue)
		implicit none
		type(SolverList), intent(in) :: List
		integer, intent(in) :: Destination
		logical :: lvalue

		select case(Destination)
			case (FirstSolutionRow)
				lvalue = List % nFirst == List % actualFirst
			case (SecondSolutionRow)
				lvalue = List % nSecond == List % actualSecond
			case (BulkSolutionRow)
				lvalue = List % nBulk == List % actualBulk
		end select
	end function



end module
